
#include <string.h>
#include "Watcher.h"
#include "LoopService.h"
#include "event2/util.h"
#include "event.h"
#include <assert.h>




EZWatcher::EZWatcher(struct event_base* evbase, const Handler& handler)
    : evbase_(evbase), attached_(false), handler_(handler) {
    event_ = new event;
    memset(event_, 0, sizeof(struct event));
}


EZWatcher::~EZWatcher() {
    _free_event();
    _close();
}

bool EZWatcher::init() {
    if (!_do_init()) {
		_close();
		return false;
    }

    ::event_base_set(evbase_, event_);
    return true;   
}


void EZWatcher::_close() {
    _do_close();
}

bool EZWatcher::_watch(int timeout) {
    struct timeval tv;
    struct timeval* timeoutval = nullptr;
    if (timeout > 0) {
		tv.tv_sec = (long)(timeout / 1000);
		tv.tv_usec = (long)(timeout % 1000) / (long)1000000;
        timeoutval = &tv;
    }

    if (attached_) {
        // When InvokerTimer::periodic_ == true, EventWatcher::Watch will be called many times
        // so we need to remove it from event_base before we add it into event_base
        if (event_del(event_) != 0) {
            // TODO how to deal with it when failed?
        }
        attached_ = false;
    }

    assert(!attached_);
    if (event_add(event_, timeoutval) != 0) {
        return false;
    }
    attached_ = true;
    return true;
}

void EZWatcher::_free_event() {
    if (event_) {
        if (attached_) {
            event_del(event_);
            attached_ = false;
        }

        delete (event_);
        event_ = nullptr;
    }
}

void EZWatcher::cancel() {
    assert(event_);
    _free_event();

    if (cancel_callback_) {
        cancel_callback_();
        cancel_callback_ = Handler();
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

PipeEventWatcher::PipeEventWatcher(LoopService* loop,
                                   const Handler& handler)
    : EZWatcher(loop->event_base(), handler)
{
    memset(pipe_, 0, sizeof(pipe_[0] * 2));
}




PipeEventWatcher::~PipeEventWatcher() {
    _close();
}

bool PipeEventWatcher::_do_init() {
    assert(pipe_[0] == 0);

    if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_) < 0) {
        int err = EVUTIL_SOCKET_ERROR();
        fprintf(stderr, "create socketpair ERROR errno=%d %s\n",err,evutil_socket_error_to_string(err));
        goto failed;
    }

    if (evutil_make_socket_nonblocking(pipe_[0]) < 0 ||
        evutil_make_socket_nonblocking(pipe_[1]) < 0) {
        goto failed;
    }

    ::event_set(event_, pipe_[1], EV_READ | EV_PERSIST,
                &PipeEventWatcher::_pipe_handle_fun, this);
    return true;
failed:
    _close();
    return false;
}

void PipeEventWatcher::_do_close() {
    if (pipe_[0] > 0) {
        EVUTIL_CLOSESOCKET(pipe_[0]);
        EVUTIL_CLOSESOCKET(pipe_[1]);
        memset(pipe_, 0, sizeof(pipe_[0]) * 2);
    }
}

void PipeEventWatcher::_pipe_handle_fun(ez_socket_t fd, short /*which*/, void* v) {
    PipeEventWatcher* e = (PipeEventWatcher*)v;
    char buf[1024];
    int n = 0;

    if ((n = ::recv(e->pipe_[1], buf, sizeof(buf), 0)) > 0) {
        e->handler_();
    }
}

bool PipeEventWatcher::async_wait() {
    return _watch(0);
}

void PipeEventWatcher::notify() {
    char buf[1] = {};

    if (::send(pipe_[0], buf, sizeof(buf), 0) < 0) {
        return;
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
TimerEventWatcher::TimerEventWatcher(LoopService* loop,
                                     const Handler& handler,
                                     int timeout)
    : EZWatcher(loop->event_base(), handler)
    , timeout_(timeout) {}


TimerEventWatcher::TimerEventWatcher(struct event_base* loop,
                                     const Handler& handler,
                                     int timeout)
    : EZWatcher(loop, handler)
    , timeout_(timeout) {}


bool TimerEventWatcher::_do_init() {
    ::event_set(event_, -1, 0, TimerEventWatcher::_time_event_handle_fun, this);
    return true;
}

void TimerEventWatcher::_time_event_handle_fun(ez_socket_t /*fd*/, short /*which*/, void* v) {
    TimerEventWatcher* h = (TimerEventWatcher*)v;
    h->handler_();
}

bool TimerEventWatcher::async_wait() {
    return _watch(timeout_);
}




